﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Text.RegularExpressions;
using UnityEngine;

public static class LogManager
{
    static object m_Lock = new object(); 
    static System.IO.FileStream m_FileStream;
    public static System.IO.FileStream FileStream
    {
        get
        {
            lock (m_Lock)
            {
                if (m_FileStream == null)
                {
                    var path = "./Log"
#if !SERVER
                        + Client.Instance.m_PlayerID
#endif
                        + ".txt";
                    if (System.IO.File.Exists(path))
                    {
                        System.IO.File.Delete(path);
                    }
                    m_FileStream = System.IO.File.Open(path, System.IO.FileMode.OpenOrCreate);
                }
            }
            return m_FileStream;
        }
    }

    public static string GetCurMethodName(int skip = 1, int stacks = 1)
    {
        string s = string.Empty;
        System.Diagnostics.StackTrace st = new System.Diagnostics.StackTrace(true);
        for (int i = skip, length = skip + stacks; i < length; i++)
        {
            var frame = st.GetFrame(i);
            if (frame == null)
            {
                continue;
            }
            try
            {
                s += GetShortFileName(frame.GetFileName()) + "." + frame.GetMethod().Name + ":" + frame.GetFileLineNumber()
#if !SERVER
                    + "\n\t";
#else
                    + "\t";
#endif
            }
            catch { }
        }
        s = s.TrimEnd('\n', '\t');
        return s;
    }

    public static string GetCurMethodName()
    {
        string s = string.Empty;
        System.Diagnostics.StackTrace st = new System.Diagnostics.StackTrace(false);
        var frame = st.GetFrame(2);
        s = frame.GetMethod().Name;
        return s;
    }

    public static string GetShortFileName(string value)
    {
        var c = Regex.Matches(value, @"[\\/](\w+).cs");
        foreach (Match item in c)
        {
            return item.Groups[1].Value;
        }
        return string.Empty;
    }

    static void Write(string msg)
    {
        msg += "\n";
        var bs = Encoding.UTF8.GetBytes(msg);
        FileStream.Write(bs, 0, bs.Length);
        FileStream.Flush();
    }

    static void LogInternal(string msg)
    {
#if SERVER
#if !RELEASE
        Console.WriteLine(msg);
#endif
#if !CLIENT
        Write(msg);
#else
#endif
#else
        UnityEngine.Debug.Log(msg);
        if (Client.Instance.m_PlayerID != 0)
        {
            Write(msg);
        }
#endif
    }

    static void ErrorInternal(string msg)
    {
#if SERVER
        Console.WriteLine(msg);
        Write(msg);
#else
        UnityEngine.Debug.LogError(msg);
        if (Client.Instance.m_PlayerID != 0)
        {
            Write(msg);
        }
#endif
    }

    public static void Log(object msg, bool needStack = false)
    {
        LogInternal(System.DateTime.Now + ":" + System.DateTime.Now.Millisecond + " " + GetCurMethodName() + " " + msg + " (" + GetCurMethodName(2, needStack ? 3 : 1) + ")");
    }

    public static void Error(object msg, bool needStack = false)
    {
        ErrorInternal(System.DateTime.Now + ":" + System.DateTime.Now.Millisecond + " [ERROR] " + GetCurMethodName() + " " + msg + " (" + GetCurMethodName(2, needStack ? 3 : 1) + ")");
    }

    public static string GetVarName<T>(Expression<Func<string, T>> exp)
    {
        return ((MemberExpression)exp.Body).Member.Name;
    }

    public static string GetLogString<T>(this List<T> list)
    {
        StringBuilder sb = new StringBuilder();
        for (int i = 0, max = list.Count; i < max; i++)
        {
            var l = list[i];
            sb.Append((i == 0 ? "" : "; ") + l.ToString());
        }
        return sb.ToString();
    }

    public static string GetHierarchy(GameObject obj)
    {
        if (obj == null) return "";
        string path = obj.name;

        while (obj.transform.parent != null)
        {
            obj = obj.transform.parent.gameObject;
            path = obj.name + "\\" + path;
        }
        return path;
    }
}
